<template>
    <transition name="el-zoom-in-top" @after-leave="$emit('dodestroy')">
        <div
                v-show="visible"
                class="el-time-panel el-popper"
                :class="popperClass">
            <div class="el-time-panel__content" :class="{ 'has-seconds': showSeconds }">
                <time-spinner
                        ref="spinner"
                        @change="handleChange"
                        :arrow-control="useArrow"
                        :show-seconds="showSeconds"
                        :am-pm-mode="amPmMode"
                        @select-range="setSelectionRange"
                        :date="date">
                </time-spinner>
            </div>
            <div class="el-time-panel__footer">
                <button
                        type="button"
                        class="el-time-panel__btn cancel"
                        @click="handleCancel">{{ t('el.datepicker.cancel') }}
                </button>
                <button
                        type="button"
                        class="el-time-panel__btn"
                        :class="{confirm: !disabled}"
                        @click="handleConfirm()">{{ t('el.datepicker.confirm') }}
                </button>
            </div>
        </div>
    </transition>
</template>

<script type="text/babel">
    import {limitTimeRange, isDate, clearMilliseconds, timeWithinRange} from 'element-ui/src/utils/date-util';
    import Locale from 'element-ui/src/mixins/locale';
    import TimeSpinner from '../basic/time-spinner';

    export default {
        mixins: [Locale],

        components: {
            TimeSpinner
        },

        props: {
            visible: Boolean,
            timeArrowControl: Boolean
        },

        watch: {
            visible(val) {
                if (val) {
                    this.oldValue = this.value;
                    this.$nextTick(() => this.$refs.spinner.emitSelectRange('hours'));
                } else {
                    this.needInitAdjust = true;
                }
            },

            value(newVal) {
                let date;
                if (newVal instanceof Date) {
                    date = limitTimeRange(newVal, this.selectableRange, this.format);
                } else if (!newVal) {
                    date = this.defaultValue ? new Date(this.defaultValue) : new Date();
                }

                this.date = date;
                if (this.visible && this.needInitAdjust) {
                    this.$nextTick(_ => this.adjustSpinners());
                    this.needInitAdjust = false;
                }
            },

            selectableRange(val) {
                this.$refs.spinner.selectableRange = val;
            },

            defaultValue(val) {
                if (!isDate(this.value)) {
                    this.date = val ? new Date(val) : new Date();
                }
            }
        },

        data() {
            return {
                popperClass: '',
                format: 'HH:mm:ss',
                value: '',
                defaultValue: null,
                date: new Date(),
                oldValue: new Date(),
                selectableRange: [],
                selectionRange: [0, 2],
                disabled: false,
                arrowControl: false,
                needInitAdjust: true
            };
        },

        computed: {
            showSeconds() {
                return (this.format || '').indexOf('ss') !== -1;
            },
            useArrow() {
                return this.arrowControl || this.timeArrowControl || false;
            },
            amPmMode() {
                if ((this.format || '').indexOf('A') !== -1) return 'A';
                if ((this.format || '').indexOf('a') !== -1) return 'a';
                return '';
            }
        },

        methods: {
            handleCancel() {
                this.$emit('pick', this.oldValue, false);
            },

            handleChange(date) {
                // this.visible avoids edge cases, when use scrolls during panel closing animation
                if (this.visible) {
                    this.date = clearMilliseconds(date);
                    // if date is out of range, do not emit
                    if (this.isValidValue(this.date)) {
                        this.$emit('pick', this.date, true);
                    }
                }
            },

            setSelectionRange(start, end) {
                this.$emit('select-range', start, end);
                this.selectionRange = [start, end];
            },

            handleConfirm(visible = false, first) {
                if (first) return;
                const date = clearMilliseconds(limitTimeRange(this.date, this.selectableRange, this.format));
                this.$emit('pick', date, visible, first);
            },

            handleKeydown(event) {
                const keyCode = event.keyCode;
                const mapping = {38: -1, 40: 1, 37: -1, 39: 1};

                // Left or Right
                if (keyCode === 37 || keyCode === 39) {
                    const step = mapping[keyCode];
                    this.changeSelectionRange(step);
                    event.preventDefault();
                    return;
                }

                // Up or Down
                if (keyCode === 38 || keyCode === 40) {
                    const step = mapping[keyCode];
                    this.$refs.spinner.scrollDown(step);
                    event.preventDefault();
                    return;
                }
            },

            isValidValue(date) {
                return timeWithinRange(date, this.selectableRange, this.format);
            },

            adjustSpinners() {
                return this.$refs.spinner.adjustSpinners();
            },

            changeSelectionRange(step) {
                const list = [0, 3].concat(this.showSeconds ? [6] : []);
                const mapping = ['hours', 'minutes'].concat(this.showSeconds ? ['seconds'] : []);
                const index = list.indexOf(this.selectionRange[0]);
                const next = (index + step + list.length) % list.length;
                this.$refs.spinner.emitSelectRange(mapping[next]);
            }
        },

        mounted() {
            this.$nextTick(() => this.handleConfirm(true, true));
            this.$emit('mounted');
        }
    };
</script>
